package com.allen.study.test_common.interceptor.impl;

import com.alibaba.fastjson.JSON;
import com.allen.study.test_common.annotation.RepeatSubmit;
import com.allen.study.test_common.constant.CacheConstants;
import com.allen.study.test_common.filter.RepeatedlyRequestWrapper;
import com.allen.study.test_common.interceptor.RepeatSubmitInterceptor;
import com.allen.study.test_common.utils.StringUtils;
import com.allen.study.test_common.utils.http.HttpHelper;
import com.allen.study.test_common.utils.redis.RedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 判断请求url和数据是否和上一次相同，
 * 如果和上次相同，则是重复提交表单。 有效时间为10秒内。
 * 
 * @author ruoyi
 */
@Component//把普通pojo实例化到spring容器中，相当于配置文件中的
@RefreshScope
public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
{
    public final String REPEAT_PARAMS = "repeatParams";

    public final String REPEAT_TIME = "repeatTime";

    // 令牌自定义标识
    @Value("${token.header}")
    private String header;

    @Autowired
    private RedisCache redisCache;

    @SuppressWarnings("unchecked")
    @Override
    public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation)
    {
        String nowParams = "";
        // 判断request对象是不是RepeatedlyRequestWrapper的实例对象
        if (request instanceof RepeatedlyRequestWrapper)
        {
            RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;
            // 取出参数（url的body里的参数）
            nowParams = HttpHelper.getBodyString(repeatedlyRequest);
        }

        // body参数为空，获取Parameter的数据
        if (StringUtils.isEmpty(nowParams))
        {
            nowParams = JSON.toJSONString(request.getParameterMap());
        }
        Map<String, Object> nowDataMap = new HashMap<String, Object>();
        // 把请求里的参数，还有请求提交时间，放进Map
        nowDataMap.put(REPEAT_PARAMS, nowParams);
        nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());

        // 请求地址（作为存放cache的key值）
        String url = request.getRequestURI();

        // 唯一值（没有消息头则使用请求地址）
        String submitKey = StringUtils.trimToEmpty(request.getHeader(header));

        // 唯一标识（指定key + url + 消息头）
        // repeat_submit: + url + submitKey
        String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + submitKey;

        // 到redis缓存里获取这个key的value
        Object sessionObj = redisCache.getCacheObject(cacheRepeatKey);
        // 如果这个key的value存在，value存的是Map<String, Object>对象，Map[url-->nowDataMap]
        if (sessionObj != null)
        {
            Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;
            // 判断value，也就是Map的key是否含有这个url，如果有的话，就证实是这个请求了
            if (sessionMap.containsKey(url))
            {
                // 取出缓存value里url对应的value，这个value也是Map，存放的是请求参数和请求时间
                Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);
                // 判断缓存里的参数和这次的参数是否相同
                // 判断缓存里的请求时间和这次的请求时间相差是否超过设置时间，这个时间是注解传过来的
                if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval()))
                {
                    // 如果都相同的话，就说明是限定时间内的重复请求
                    return true;
                }
            }
        }
        // 如果不是重复请求，就把上面拼接的key当做key，存有请求参数和请求时间的Map当做value。存进缓存
        Map<String, Object> cacheMap = new HashMap<String, Object>();
        cacheMap.put(url, nowDataMap);
        redisCache.setCacheObject(cacheRepeatKey, cacheMap, annotation.interval(), TimeUnit.MILLISECONDS);
        // 返回false，表示这次不是重复提交
        return false;
    }

    /**
     * 判断参数是否相同
     */
    private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap)
    {
        String nowParams = (String) nowMap.get(REPEAT_PARAMS);
        String preParams = (String) preMap.get(REPEAT_PARAMS);
        return nowParams.equals(preParams);
    }

    /**
     * 判断两次间隔时间
     */
    private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap, int interval)
    {
        long time1 = (Long) nowMap.get(REPEAT_TIME);
        long time2 = (Long) preMap.get(REPEAT_TIME);
        if ((time1 - time2) < interval)
        {
            return true;
        }
        return false;
    }
}
